<?php
/**
 * Created by PhpStorm.
 * User: BlueIdeaLirui
 * Date: 2019/11/11
 * Time: 10:13
 */

namespace business\bigdata\services\queues;

use business\base\caches\DataCache;
use business\bigdata\repositories\MaterialRepository;
use business\elasticsearch\models\MaterialBasicModel;
use common\queue\QueueConstants;
use common\utils\DateTimeUtils;
use yii\helpers\ArrayHelper;

/**
 * 材料消费端服务
 * Class MaterialConsumerService
 * @package business\bigdata\services
 */
class MaterialConsumerService extends BaseService
{
    /**
     * 指定模块
     */
    const MODULE = 'material';

    /**
     * 材料消费端服务构造器
     * MaterialConsumerService constructor.
     */
    public function __construct()
    {
        $this->createIndex();
        parent::__construct();
    }

    /**
     * 创建索引
     * @return void
     */
    protected function createIndex()
    {
        $model = new MaterialBasicModel();
        !$model->existsIndex() && $model->createIndex();
    }

    /**
     * 消费端实现
     * @throws \yii\base\InvalidConfigException
     */
    public function process
    (
        $values,
        $key,
        $messageId
    ) {
        $this->consumeQueue($this->callbackMethod, $values, $key, $messageId);
    }

    /**
     * 消费队列
     * @param callable $sucessedCallback
     * @return bool|mixed
     * @throws \yii\base\InvalidConfigException
     */
    protected function consumeQueue
    (
        callable $sucessedCallback,
        $values = '',
        $key = '',
        $messageId
    ) {
        $needUpdateIndexMaterials = [];
        $needDeleteIndexMaterials = [];

        $materialRepository = new MaterialRepository();
        $dataCache          = new DataCache();

        $queueCacheName     = self::MODULE . '-' . QueueConstants::QUEUE_CHACHE_NAME;

        if ($dataCache->existsValue('consumer-' . $queueCacheName . ':' . $key
                , serialize(['values' => $values, 'created_on' => DateTimeUtils::now()])) == 1) {
            // 清除缓存记录
            $dataCache->delRecordByHash($queueCacheName, $key);

            $waitUpdateMaterialsNames = explode(self::DELIMITER, $values);

            if (!empty($waitUpdateMaterialsNames)) {
                $start = 0;
                $sizeCount = (self::MAX_COUNT_PER_INDEX / self::MAX_COUNT_PER_QUERY);

                while (1) {
                    $start++;

                    $startTime = explode(' ', microtime());

                    $slicceUpdateMaterialsNames = array_slice($waitUpdateMaterialsNames, ($start - 1) * $sizeCount, $sizeCount);

                    if (!empty($slicceUpdateMaterialsNames)) {
                        $materialBasics = $materialRepository->getMaterialBasicInfoByNames($slicceUpdateMaterialsNames);
                        if (is_array($materialBasics)) {
                            $basicNames = ArrayHelper::index($materialBasics, 'materialName');
                            $basicKey   = array_keys($basicNames);

                            // 判断是否有删除的材料数据
                            foreach ($slicceUpdateMaterialsNames as $name) {
                                if (!in_array($name, $basicKey)) {
                                    array_push($needDeleteIndexMaterials, ['uid' => $basicNames[$name]['uid'], 'materialName' => $name]);
                                }
                            }
                        }

                        $this->getMaterialDocumentArray($materialBasics, $needUpdateIndexMaterials, $needDeleteIndexMaterials);

                        $endTime = explode(' ', microtime());
                        $thisTime = $endTime[0] + $endTime[1] - ($startTime[0] + $startTime[1]);
                        echo "材料供应商-增量索引更新：每" . $sizeCount . "条记录执行数据拼接耗时：" . round($thisTime, 3) . " 秒" . PHP_EOL;

                        // 每x次轮训刷新一次搜索引擎
                        if ($start % $sizeCount == 0
                            && (!empty($needUpdateIndexMaterials) || !empty($needDeleteIndexMaterials))
                        ) {
                            //刷新搜索引擎
                            $this->updateDataIndex($needUpdateIndexMaterials, 'update');
                            $this->updateDataIndex($needDeleteIndexMaterials, 'delete');

                            $needUpdateIndexMaterials = [];
                            $needDeleteIndexMaterials = [];
                        }

                        usleep(self::MICRO_SECOND);
                    } else {
                        // 清除缓存记录
                        $dataCache->delRecordByHash($queueCacheName, $key);
                        break;
                    }
                }
            }

            // 防止最后的未更新
            $this->updateDataIndex($needUpdateIndexMaterials, 'update');
            $this->updateDataIndex($needDeleteIndexMaterials, 'delete');

            // 清除缓存记录
            $dataCache->delRecordByHash($queueCacheName, $key);

            if (is_callable($sucessedCallback)) {
                return call_user_func($sucessedCallback, $needUpdateIndexMaterials);
            }

            return true;
        }

        return true;
    }

    /**
     * 处理需要更新的材料数据
     * @param $needUpdateIndex
     */
    protected function handleUpdateSet
    (
        $needUpdateIndex
    ) {
        $updateIndex = [];

        if (!empty($needUpdateIndex)) {
            $updateIndex = $needUpdateIndex;
        }

        return $updateIndex;
    }

    /**
     * 更新索引
     * @param array $needUpdateIndexArrays
     * @param $jobCreatedTime
     */
    protected function updateIndex
    (
        $needUpdateIndexArrays = [],
        $jobCreatedTime
    ) {
        if (!empty($needUpdateIndexArrays)) {
            $indexStartTime = explode(' ', microtime());

            foreach ($needUpdateIndexArrays as $needUpdateIndex) {
                $id              = $needUpdateIndex['uid'];
                $materialName    = $needUpdateIndex['materialName'];

                try {
                    $materialBasicModel = MaterialBasicModel::findOne($id);
                    if (empty($materialBasicModel)) {
                        $materialBasicModel = new MaterialBasicModel();
                        $materialBasicModel->primaryKey = $id;
                    }
                    $materialBasicModel->setAttributes($this->handleUpdateSet($needUpdateIndex), false);
                    $rs = $materialBasicModel->save();
                    if (!$rs) {
                        \Yii::error('update es_material failed! material_id:' . $id);
                    }
                    echo 'success material_id is ' . $id . PHP_EOL;
                } catch (\Exception $e) {
                    echo 'update failed! material_id:' . $id . PHP_EOL;
                    $title = isset($materialName) ? $materialName : '';
                    $this->saveUnIndexData('es_material', $title, $id, $e);
                }
            }

            $indexEndTime = explode(' ', microtime());
            $indexThisTime = $indexEndTime[0] + $indexEndTime[1] - ($indexStartTime[0] + $indexStartTime[1]);
            $indexThisTime = round($indexThisTime, 3);
            echo  "update索引耗时：" . $indexThisTime . " 秒" . PHP_EOL;
        }
    }

    /**
     * 删除索引
     * @param array $needDeleteIndexArrays
     * @param $jobCreatedTime
     * @throws \yii\elasticsearch\Exception
     */
    protected function deleteIndex
    (
        $needDeleteIndexArrays = [],
        $jobCreatedTime
    ) {
        if (!empty($needDeleteIndexArrays)) {
            $indexStartTime = explode(' ', microtime());
            foreach ($needDeleteIndexArrays as $needDeleteIndex) {
                $id              = $needDeleteIndex['uid'];
                $materialName    = $needDeleteIndex['materialName'];

                try{
                    $materialBasicModel = MaterialBasicModel::findOne($id);
                    if ($materialBasicModel) {
                        $rs = $materialBasicModel->deleteAll(['_id' => $id]);

                        if (!$rs) {
                            \Yii::error('delete es_supplier failed! material_id:' . $id);
                        }
                        echo 'success material_id is ' . $id . PHP_EOL;
                    }
                } catch (\Exception $e) {
                    echo 'delete failed! material_id:' . $id . PHP_EOL;
                    $title = isset($materialName) ? $materialName : '';
                    $this->saveUnIndexData('es_material', $title, $id, $e);
                }
            }

            $indexEndTime = explode(' ', microtime());
            $indexThisTime = $indexEndTime[0] + $indexEndTime[1] - ($indexStartTime[0] + $indexStartTime[1]);
            $indexThisTime = round($indexThisTime, 3);
            echo "  删除索引耗时：" . $indexThisTime . " 秒" . PHP_EOL;
        }
    }

    /**
     * 获取材料索引document
     * @param $materialBasics
     * @param $needUpdateIndexMaterials
     * @param array $needDeleteIndexMaterials
     */
    protected function getMaterialDocumentArray
    (
        $materialBasics,
        & $needUpdateIndexMaterials,
        & $needDeleteIndexMaterials = []
    ) {
        foreach ($materialBasics as &$materials) {
            $priceInfo = $this->getMaterialPriceLog($materials['materialSkuCodes']);
            $materials['priceInfo'] = json_encode($priceInfo, 320);

            if ($materials['is_deleted'] == 0) {
                array_push($needUpdateIndexMaterials, $materials);
            } else {
                array_push($needDeleteIndexMaterials, ['uid' => $materials['uid'], 'materialName' => $materials['material_name']]);
            }
        }
    }

    /**
     * 获取材料价格
     * @param $materialSkuCodes
     * @return array
     */
    protected function getMaterialPriceLog($materialSkuCodes)
    {
        $materialRepository = new MaterialRepository();
        $data = $materialRepository->getMaterialPrice($materialSkuCodes);

        $result = [];
        foreach ($data as $rv) {
            if (!array_key_exists($rv['material_sku_code'], $result)) {
                $result[$rv['material_sku_code']] = $rv;
            }
        }

        return array_values($result);
    }
}
